package com.momalib.ning2_nocode.services.impl;

import com.momalib.ning2_nocode.beans.NncActionInfo;
import com.momalib.ning2_nocode.beans.NncSourceConfig;
import com.momalib.ning2_nocode.config.NncConfig;
import com.momalib.ning2_nocode.services.SourceParseService;
import com.momalib.ning2_nocode.utils.CommonFileUtil;
import com.momalib.ning2_nocode.utils.CommonUtils;
import com.momalib.ning2_nocode.utils.NncConfUtil;
import com.momalib.ning2_nocode.utils.XmlUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

@Log4j2
@Service
public class SourceParseServiceImpl implements SourceParseService {

    @Autowired
    NncConfig nncConfig;

    @Autowired
    NncConfUtil nncConfUtil;

    @Override
    public void handleSourceBuild(String tmplRootPath, NncSourceConfig sourceConfig) {
        if (sourceConfig == null || sourceConfig.getName() == null) {
            return;
        }
        String sourcePath = nncConfig.getSrcFilePath() + "/" + sourceConfig.getName();
        String sourceRootPath = new File(sourcePath).getAbsolutePath();

        List<String> allFilePaths = null;
        try {
            allFilePaths = handleFrameActions(tmplRootPath, sourceConfig.getIgnore(), sourceConfig.getFrameActions(), sourceRootPath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
//            allFilePaths.addAll(fragmentNewFiles(destRootPath, sourceConfig.getFrameActions()));
        for (String filePath: allFilePaths) {
            File newFile = new File(filePath);
            if (!newFile.exists() || !newFile.isFile()) {
                continue;
            }
            try {
                handleFileActions(sourceConfig.getFileActions(), newFile);
                for (NncActionInfo actionInfo: sourceConfig.getSpecActions()) {
                    String actionSourcePath = new File(nncConfUtil.convertPath(tmplRootPath + "/target", actionInfo.getSource())).getAbsolutePath();
                    if (actionSourcePath.equals(filePath)) {
                        handleSpecAction(actionInfo, newFile);
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private void handleSpecAction(NncActionInfo actionInfo, File destFile) throws IOException {
        String command = actionInfo.getCommand();
        String srcString = Files.readString(Paths.get(destFile.getAbsolutePath()));
        String resultString = srcString;
        log.info(command);
        if ("handleXml".equals(command)) {
            try {
                resultString = handleXml(destFile, actionInfo);
            } catch (Exception e) {
                e.printStackTrace();
            }
//            resultString = Files.readString(Paths.get(destFile.getAbsolutePath()));
        } else if ("handleJava".equals(command)) {
            resultString = handleJava(srcString, actionInfo);
        }
        Files.writeString(Paths.get(destFile.getAbsolutePath()), resultString, StandardCharsets.UTF_8);
    }

    private List<String> handleFrameActions(String tmplRootPath, List<String> ignores, List<NncActionInfo> frameActions, String sourceRootPath) throws IOException {
        File destRootFile = new File(tmplRootPath + "/target");
        String destRootPath = destRootFile.getAbsolutePath();
        if (destRootFile.exists()) {
            FileUtils.deleteDirectory(destRootFile);
        }
        if (!destRootFile.mkdirs()) {
            return new ArrayList<>();
        }
        List<String> allFilePaths = CommonFileUtil.getFileFromPath(sourceRootPath);
        List<String> resultList = new ArrayList<>();

        for (String srcFilename: allFilePaths) {
            String destFilename = srcFilename.replace(sourceRootPath, destRootPath);
            String sourceRelativePath = srcFilename.replace(sourceRootPath, "");

            if (nncConfUtil.isIgnore(ignores, sourceRootPath, srcFilename)) {
                continue;
            }
            for (NncActionInfo action: frameActions) {
                String command = action.getCommand();
                if ("replacePath".equals(command)) {
                    destFilename = destRootPath + "/" + sourceRelativePath.replace(action.getTarget(), action.getContent());
                }
            }
            File destFile = new File(destFilename);
            File destParentFile = destFile.getParentFile();
            boolean createOK;
            if (!destParentFile.exists()) {
                createOK = destParentFile.mkdirs();
            } else {
                createOK = true;
            }
            if (createOK) {
                createOK = destFile.createNewFile();
            }
            if (createOK) {
                CommonFileUtil.copyFileUsingFileStreams(new File(srcFilename), destFile);
                resultList.add(destFile.getAbsolutePath());
            }
        }

        for (NncActionInfo action: frameActions) {
            String command = action.getCommand();
            String active = action.getActive();
            if ("handleJava".equals(command) && "addClass".equals(active)) {
                String classPath = handleAddJavaClass(tmplRootPath, destRootPath, action);
                if (classPath != null) {
                    resultList.add(classPath);
                }
            }
        }

        return resultList;
    }

    private String handleAddJavaClass(String tmplRootPath, String destRootPath, NncActionInfo action) throws IOException {
        String classPath = nncConfUtil.convertPath(tmplRootPath, action.getSource());
        File classFile = new File(classPath);

        String distPath = destRootPath + "/" + action.getTarget();
        File destParentFile = new File(distPath);
        boolean createOK = false;
        if (!destParentFile.exists()) {
            createOK = destParentFile.mkdirs();
        } else {
            createOK = true;
        }
        File destFile = null;
        if (createOK) {
            destFile = new File(distPath + "/" + classFile.getName());
            createOK = destFile.createNewFile();
        }
        if (destFile == null) {
            return null;
        }
        if (createOK) {
            CommonFileUtil.copyFileUsingFileStreams(classFile, destFile);
        }
        return destFile.getAbsolutePath();
    }

    @Override
    public void handleFileActions(List<NncActionInfo> actions, File destFile) throws IOException {
        if (!destFile.exists()) {
            return;
        }

        if (actions.size() > 0) {
            Charset charset = CommonFileUtil.detectEncoding(destFile);
            if (charset == null) {
//                log.error("charset null => " +destFile.getAbsolutePath());
                return;
            }
            if (!charset.equals(StandardCharsets.UTF_8) && !charset.equals(StandardCharsets.US_ASCII)) {
//                log.error("charset " + charset.displayName() + " => " +destFile.getAbsolutePath());
                return;
            }
            String srcString = Files.readString(Paths.get(destFile.getAbsolutePath()), charset);
            String resultString = srcString;
            for (NncActionInfo action: actions) {
                String command = action.getCommand();
                if ("replace".equals(command)) {
                    resultString = resultString.replace(action.getTarget(), action.getContent());
                }
            }

            Files.writeString(Paths.get(destFile.getAbsolutePath()), resultString, charset);
//            try (BufferedReader reader = new BufferedReader(new FileReader(srcFilename));
//                 BufferedWriter writer = new BufferedWriter(new FileWriter(destFilename))) {
//                String line;
//                while ((line = reader.readLine()) != null) {
//                    String newLine = line;
//                    for (NncActionInfo action: fileActions) {
//                        String command = action.getCommand();
//                        if ("replace".equals(command)) {
//                            newLine = line.replace(action.getFrom(), action.getTo());
//                        }
//                    }
//                    writer.write(newLine);
//                    writer.newLine();
//                }
//            } catch (IOException e) {
//                e.printStackTrace();
//            }

        }
    }

    private String handleJava(String srcString, NncActionInfo action) {
        return srcString;
    }

    private String handleXml(File srcFile, NncActionInfo action) {
        try {
            // 创建 DocumentBuilderFactory
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            // 创建 DocumentBuilder
            DocumentBuilder builder = factory.newDocumentBuilder();
            // 解析现有的 XML 文件来构建 Document
            Document document = builder.parse(srcFile);

            XPathFactory xpathFactory = XPathFactory.newInstance();
            XPath xpath = xpathFactory.newXPath();
            Node targetNode = (Node) xpath.evaluate(action.getTarget(), document, XPathConstants.NODE);

            System.out.println(CommonUtils.obj2JsonStr(action));
            if ("removeNode".equals(action.getActive())) {
                targetNode.getParentNode().removeChild(targetNode);
            } else if ("appendNode".equals(action.getActive())) {
                String content = action.getContent().trim(); // Trim leading/trailing whitespace
                // Optional: Log the content for debugging
                System.out.println("Appending XML Content: " + content);

                Node importedNode = document.importNode(XmlUtil.convertTextToNode(document, content), true);

                targetNode.appendChild(importedNode);
            } else if ("replaceNode".equals(action.getActive())) {
                Node parentNode = targetNode.getParentNode();

                String content = action.getContent().trim(); // Trim leading/trailing whitespace
                // Optional: Log the content for debugging
                System.out.println("Replacing with XML Content: " + content);
                // 将解析出的节点导入到目标文档中
                Node importedNode = document.importNode(XmlUtil.convertTextToNode(document, content), true);
                parentNode.replaceChild(importedNode, targetNode);
            } else if ("setContentText".equals(action.getActive())) {
                targetNode.setTextContent(action.getContent().trim());
            }

            return XmlUtil.convertDocumentToString2(document);
        } catch (SAXException e) {
            throw new RuntimeException("XML Parsing error: " + e.getMessage(), e);
        } catch (IOException e) {
            throw new RuntimeException("File I/O error: " + e.getMessage(), e);
        } catch (ParserConfigurationException e) {
            throw new RuntimeException("Parser configuration error: " + e.getMessage(), e);
        } catch (XPathExpressionException e) {
            throw new RuntimeException("XPath evaluation error: " + e.getMessage(), e);
        }
    }

    public static void processFile(String srcFilename, String destFilename, String oldString, String newString) {
        try (BufferedReader reader = new BufferedReader(new FileReader(srcFilename));
             BufferedWriter writer = new BufferedWriter(new FileWriter(destFilename))) {
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line.replace(oldString, newString));
                writer.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
